package org.stars.dao.sqlmapper.macro;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.stars.config.Config;
import org.stars.dao.exception.parser.MacroArgsNotAdmittedException;
import org.stars.dao.exception.parser.MacroArgsRequiredException;
import org.stars.dao.exception.parser.MacroBodyNotAdmittedException;
import org.stars.dao.exception.parser.MacroBodyRequiredException;
import org.stars.dao.sqlmapper.SqlDefinition;
import org.stars.dao.sqlmapper.SqlMacro;
import org.stars.dao.sqlmapper.SqlMapper;
import org.stars.dao.sqlmapper.parser.TemplateParser;
import org.stars.dao.sqlmapper.parser.TemplateParser2;

/**
 * The Class MacroExecutor.
 */
public abstract class MacroExecutor {
	

	/**
	 * Execute.
	 *
	 * @param context the context
	 * @param sqlMapper the sql mapper
	 * @param sqlCurrent the sql current
	 * @param sqlParentMacro the sql parent macro
	 * @param sqlMacro the sql macro
	 * @return the string
	 * @throws Exception the exception
	 */
	public static String execute(MacroEvaluation context, SqlMapper sqlMapper, SqlDefinition sqlCurrent, SqlMacro sqlParentMacro, SqlMacro sqlMacro) throws Exception
	{
		Log log=LogFactory.getLog(MacroExecutor.class);
		MacroDefinition def=sqlMacro.getType().getClass().getAnnotation(MacroDefinition.class);
						
		if (context==def.evaluation())
		{
			// check if in macro def body is not admitted 
			if (def.body()==MacroParam.NOT_ADMITTED && (sqlMacro.getTemplateText().trim().length()>0 || sqlMacro.getParts().size()>0))
			{
				//assert: body is not admitted!
				Exception e=new MacroBodyNotAdmittedException(sqlMapper, sqlCurrent, sqlMacro);
				log.error(e.getMessage());
				throw(e);
			}
			
			if (def.body()==MacroParam.REQUIRED && (sqlMacro.getTemplateText().trim().length()==0 || sqlMacro.getParts().size()==0))
			{
				//assert: body is required!
				Exception e=new MacroBodyRequiredException(sqlMapper, sqlCurrent, sqlMacro);
				log.error(e.getMessage());
				throw(e);
			}
			
			//check parent macro
			if (def.admittedParents().length>0)
			{
				Set<String> admittedParentId=new TreeSet<String>();
				List<String> temp=Arrays.asList(def.admittedParents());
				admittedParentId.addAll(temp);
				
				if (sqlParentMacro!=null)
				{
					Map<String, Macro> macroMap = Config.getDaoConfig().getMacroMap();
					Macro parentMacro=sqlParentMacro.getType();
					boolean found=false;
					// we have to check parent macro
					for(String item: def.admittedParents())
					{
						Macro macro=macroMap.get(item);
						
						if (macro.equals(parentMacro))
						{
							// ok
							found=true;
							break;
						}
					}
					
					if (found)
					{
						// assert ok
					} else {
						//
					}
				} else {
					
				}
			} 
			
			// check if in macro def body is not admitted 
			if (def.arguments()==MacroParam.NOT_ADMITTED && (sqlMacro.getTemplateArgs().trim().length()>0 || sqlMacro.getArgs().size()>0))
			{
				//assert: args is not admitted!
				Exception e=new MacroArgsNotAdmittedException(sqlMapper, sqlCurrent, sqlMacro);
				log.error(e.getMessage());
				throw(e);
			}
			
			if (def.arguments()==MacroParam.REQUIRED && (sqlMacro.getTemplateArgs().length()==0 || sqlMacro.getArgs().size()==0))
			{
				//assert: args is required!
				Exception e=new MacroArgsRequiredException(sqlMapper, sqlCurrent, sqlMacro);
				log.error(e.getMessage());
				throw(e);
			}
			
			ReturnState state=sqlMacro.getType().execute(sqlMapper, sqlCurrent, sqlMacro);
			log.debug(">>> "+sqlMacro.getType().toString());
			if (state==ReturnState.DO_NOTHING)
			{
				
			} else {
				if (state==ReturnState.EVALUATE_TEMPLATE)
				{
					TemplateParser.parse(sqlCurrent, sqlMacro);
				}
				
				if(state==ReturnState.EVALUATE_TEMPLATE || state==ReturnState.EVALUATE_PARTS)
				{
					TemplateParser2.parse(MacroEvaluation.SOURCE_PROCESSING, sqlCurrent, sqlMacro);
				}
			}
			
		}
		return "";
	}
}
